﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Dynamic;
using System.Text;
using Newtonsoft.Json;
using System.Collections;
using System.Text.RegularExpressions;

namespace StateManager
{
    /// <summary>
    /// 状态机管理器
    /// </summary>
    public partial class SManager : IDisposable
    {
        /// <summary>
        /// 使实例中可以访问主程序
        /// </summary>
        public object Owner;
        /// <summary>
        /// 主配置文件Json
        /// </summary>
        public Newtonsoft.Json.Linq.JObject JObject;
        /// <summary>
        /// 主配置文件的标准配置
        /// </summary>
        public SManagerConfig Config;
        /// <summary>
        /// 工作目录，各配置文件建议放在此目录
        /// </summary>
        public string ProjectDir;
        /// <summary>
        /// 主配置文件名
        /// </summary>
        public string CfgFileName;
        /// <summary>
        /// 实例清单
        /// </summary>
        internal Dictionary<string, SInfo> _SInfos = new Dictionary<string, SInfo>();
        internal object lock_SInfos = new object();
        public IReadOnlyDictionary<string, SInfo> SInfos => _SInfos as IReadOnlyDictionary<string, SInfo>;
        /// <summary>
        /// 隔离环境
        /// </summary>
        private Dictionary<string, AppDomain> Domains = new Dictionary<string, AppDomain>();


        SEngine _engine;
        /// <summary>
        /// 事件总出口
        /// </summary>
        public Action<SManager, SEventType> OnEvent;
        public bool Auto { get { return _engine.Auto; } set { _engine.Auto = value; } }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public SInfo GetSInfo(string name)
        {
            //if (!_SInfos.ContainsKey(name)) throw new Exception("不存在实例:" + name);
            if (!_SInfos.ContainsKey(name))
                return null;
            return _SInfos[name];
        }
        /// <summary>
        /// 获取实例对象
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public object GetSObject(string name)
        {
            return GetSInfo(name)?.Object;
        }
        public AppDomain GetDomain(string Name)
        {
            if (Domains.ContainsKey(Name))
            {
                return Domains[Name];
            }
            else
                return null;
            throw new Exception("不存在程序域:" + Name);
        }
        /// <summary>
        /// 保存主配置文件
        /// </summary>
        /// <param name="strongTypeMode">保存模式。是：强类型，会丢失自定义配置内容。否：按JObject内容序列化保存，可用于二次开发时扩展</param>
        public void SaveConfigue(bool strongTypeMode = false)
        {
            string saveJson = "";
            if (strongTypeMode)
            {
                saveJson = JsonFromObj(Config, Config.GetType());
            }
            else
            {
                saveJson = JObject.ToString();
            }
            System.IO.File.WriteAllText(CfgFileName, saveJson);

            //保存每个config
            string cfgDir = Path.Combine(this.ProjectDir, Config.ConfigsDir);
            if (!Directory.Exists(cfgDir))
            {
                Directory.CreateDirectory(cfgDir);
            }
            foreach (var si in Config.SInfos)
            {
                if (si.Config == null)
                    continue;
                string cfgFile = Path.Combine(this.ProjectDir, Config.ConfigsDir, si.Name + ".json");
                string t = JsonFromObj(si.Config, si.Config.GetType());
                File.WriteAllText(cfgFile, t);
            }
        }
        public void ClearUpConfigs()
        {
            //删除主配置文件中没有的
            string cfgDir = Path.Combine(this.ProjectDir, Config.ConfigsDir);
            var fs = Directory.GetFiles(cfgDir);
            var todel = fs.Except(Config.SInfos.Select(s => Path.Combine(cfgDir, s.Name + ".json")));
            foreach (string file in todel)
            {
                if (File.Exists(file))
                {
                    File.Delete(file);
                }
            }
        }

        /// <summary>
        /// 创建状态机服务
        /// </summary>
        /// <param name="logFun">写日志函数。参数1：模块名；参数2：日志内容；参数3：是否为操作日志。如果为null则使用内置写文件</param>
        /// <param name="eventFun">系统事件回调</param>
        /// <param name="owner">创建者</param>
        /// <param name="paused">创建时暂停，后续手动开启</param>
        /// <param name="projectDir">工作目录</param>
        /// <param name="cfgFileName">主配置文件</param>
        public SManager(Action<string, string, bool> logFun = null, Action<SManager, SEventType> eventFun = null, object owner = null, bool paused = false, string projectDir = "./", string cfgFileName = "default.json")
        {
            OnEvent = eventFun;
            this.Owner = owner;
            this.ProjectDir = projectDir;
            if (this.ProjectDir == "./")
                this.ProjectDir = AppDomain.CurrentDomain.BaseDirectory;
            if (cfgFileName == "")
                cfgFileName = "default.json";
            this.CfgFileName = Path.Combine(this.ProjectDir, cfgFileName);

            _logFun += logFun;

            #region 解析json文件

            if (!System.IO.File.Exists(this.CfgFileName))
            {
                //生成默认的标准配置
                Config = new SManagerConfig();
                SaveConfigue();
            }
            else
            {
                string jText = System.IO.File.ReadAllText(this.CfgFileName);
                jText = ClearJsonComment(jText);
                //主配置文件json，便于二次开发补充
                JObject = Newtonsoft.Json.Linq.JObject.Parse(jText);
                //主配置文件的标准必须配置
                Config = JsonToObject<SManagerConfig>(jText);
            }


            #endregion

            #region 设置管理线程及全局配置

            ThreadPool.SetMinThreads(1, 1);
            ThreadPool.SetMaxThreads(Config.MaxThreads, Config.MaxThreads);
            _log2file = Config.Log2File;

            #endregion

            #region 加载所有程序集
            foreach (var tf in Config.TypeFiles)
            {
                TypeFileCache.AddCache(tf.Key, tf.Value, s => SystemLog($"成功加载程序集：{s}"));
            }
            #endregion

            #region 创建所有实例，根据实例是否支持IState<>相关
            foreach (var csi in Config.SInfos)
            {
                //Config里的SInfo没有执行构造函数，要新建SInfo后map一下,替换Config
                SInfo si = new SInfo();
                MapDataMembers(csi, si);

                JObject jo = JObject[SManagerConfig.STRING_SInfos].Select(jt => jt as JObject).Where(j => j["Name"].ToString() == csi.Name).FirstOrDefault();
                si.JObject = jo;
                AddSInfo(si);
            };

            #endregion
            #region 获取实例关联对象
            foreach (SInfo si in SInfos.Values)
            {
                LoadLinkObjFromJsonLink(si);
            }
            SaveConfigue();
            #endregion

            #region 执行实例StateInit()
            foreach (SInfo si in _SInfos.Values)
            {
                InitSObject(si);
            }
            #endregion

            _engine = new SEngine(this, Config.Auto && !paused);

            string autoStr = Config.Auto && !paused ? "自动" : "手动";
            SystemLog($"状态机管理线程启动成功，总开关：{autoStr}");

        }
        /// <summary>
        /// 手动添加一个实例，添加后需要运行InitSObject(si)才可以被自动执行
        /// </summary>
        /// <param name="si"></param>
        /// <exception cref="Exception"></exception>
        public void AddSInfo(SInfo si)
        {
            //反射实例
            object obj = null;
            try
            {
                if (_SInfos.ContainsKey(si.Name))
                    throw new Exception("不允许创建同名实例");
                if (string.IsNullOrEmpty(si.Type))
                    throw new Exception($"未定义Type");
                Type findtype = TypeFileCache.GetTypeByName(si.Type);
                if (findtype == null)
                    throw new Exception($"未找到{si.Type}类型");
                obj = Activator.CreateInstance(findtype);
                if (obj != null)
                {
                    SystemLog($"创建实例[{si.Name}]成功");
                }
                else
                    throw new Exception($"创建实例失败，类型[{si.Type}]");
            }
            catch (Exception E)
            {
                throw new Exception(string.Format("创建对象[{0}]的组件时出错：{1}", si.Name, E.Message));
            }
            si.Object = obj;
            si.Initialized = false;
            si.Manager = this;

            //如果为ISate<TConfig>，设置si.Config
            var inter = obj.GetType().GetInterfaces().Where(t => t.IsConstructedGenericType && t.GetGenericTypeDefinition() == typeof(IState<>))
                .FirstOrDefault();
            Type typeTConfig = inter?.GetGenericArguments().FirstOrDefault();
            if (typeTConfig != null)
            {
                string cfgFile = Path.Combine(this.ProjectDir, Config.ConfigsDir, si.Name + ".json");
                if (File.Exists(cfgFile))
                {
                    string jText = System.IO.File.ReadAllText(cfgFile);
                    jText = ClearJsonComment(jText);
                    si.Config = JsonToObject(typeTConfig, jText);
                }
                else
                {
                    if (typeTConfig == typeof(string))
                    {
                        si.Config = "";
                    }
                    else
                    {
                        var ucfg = Activator.CreateInstance(typeTConfig);
                        si.Config = ucfg;
                    }
                }

                ////创建XX<T>实例
                //Type genType = typeof(SInfo<>).MakeGenericType(cfgType);
                //SInfo usi = Activator.CreateInstance(genType) as SInfo;
                ////设置实例的属性
                //PropertyInfo pConfig = typeTConfig.GetProperty("Config", BindingFlags.Instance | BindingFlags.Public);
                //pConfig.SetValue(usi,).Invoke(obj, null);
                //string sas = JsonFromObj(Config, new Type[] { cfgType });
                //MapDataMembers(si,usi);
                //si = (SInfo)usi;
            }

            //添加进系统
            lock (lock_SInfos)
            {
                _SInfos[si.Name] = si;
            }
            int index = Config.SInfos.FindIndex(s => s.Name == si.Name);
            if (index >= 0)
            {
                //不应该有重复
                //Config.SInfos[index] = si;
            }
            else
            {
                Config.SInfos.Add(si);
            }
            //如果是后加的，需要补充进SManager.JObject[]中
            JObject jo = JObject[SManagerConfig.STRING_SInfos].Select(jt => jt as JObject).Where(j => j["Name"].ToString() == si.Name).FirstOrDefault();
            //JObject jo = (JObject)JObject["SInfo"].Where(jt => (jt as JObject)["Name"].ToString() == si.Name).FirstOrDefault();
            //如果SManager.JObject[]列表没有，添加默认
            if (jo == null)
            {
                //强类型标准配置的默认值
                si.JObject = JObject.Parse(JsonFromObj(si, typeof(SInfo)));
                (JObject[SManagerConfig.STRING_SInfos] as JArray).Add(si.JObject);
            }

            LoadLinkObjFromJsonLink(si);

            OnEvent?.Invoke(this, SEventType.SManagerAdded);
        }
        public Task DeleteNode(SInfo si)
        {
            Task task = new Task(() =>
            {
                lock (lock_SInfos)
                {
                    _SInfos.Remove(si.Name);
                }
                while (si.Working)
                {
                    Thread.Sleep(100);
                }
                UInitSObject(si);
                si.Object = null;
                SystemLog($"删除实例[{si.Name}]成功");
            });
            task.Start();
            OnEvent?.Invoke(this, SEventType.SManagerDeleted);
            return task;
        }

        /// <summary>
        /// 结束状态机，并释放资源
        /// </summary>
        public virtual void Dispose()
        {
            _engine.Dispose();

            foreach (SInfo si in _SInfos.Values)
            {
                UInitSObject(si);
                si.Object = null;
            }
            _SInfos.Clear();
            _SInfos = null;
        }
        /// <summary>
        /// 删除//开头的注释行
        /// </summary>
        /// <param name="json"></param>
        /// <returns></returns>

        /// <summary>
        /// 初始化每个实例
        /// </summary>
        /// <param name="so"></param>
        /// <exception cref="Exception"></exception>
        public void InitSObject(SInfo si)
        {
            try
            {
                (si.Object as IState)?.StateInit(si);
                si.Initialized = true;
            }
            catch (Exception E)
            {
                throw new Exception("StateInit错误：" + si.Name + "," + E.Message);
            }

        }
        void UInitSObject(SInfo si)
        {
            try
            {
                (si.Object as IState)?.StateUInit(si);
            }
            catch (Exception E)
            {
                throw new Exception("StateUInit错误：" + si.Name + "," + E.Message);
            }
        }
        /// <summary>
        /// AddNode时尝试从JObject添加到Link，初始化完成时会补加一次
        /// </summary>
        /// <param name="si"></param>
        void LoadLinkObjFromJsonLink(SInfo si)
        {
            if (si.JObject.ContainsKey("Link"))
            {
                JObject jo = (JObject)si.JObject["Link"];

                IDictionary<string, object> dic = si.Link;

                foreach (JProperty jp in jo.Properties())
                {
                    object obj = GetSObject(jp.Value.ToString());
                    if (obj != null)
                    {
                        dic[jp.Name] = obj;
                    }
                }
            }
        }
    }

}
